﻿using FluorineFx.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;
using System.Xml;

namespace lxwmvc
{
    public class WeiXin_QiYeHao
    {
        /// <summary>
        /// 企业号的设置
        /// </summary>
        /// <param name="corpid"></param>
        /// <param name="corpsecret"></param>
        /// <param name="key"></param>
        public WeiXin_QiYeHao(string corpid, string corpsecret, string key, string keypwd,string domain)
        {
            this.corpid = corpid;
            this.corpsecret = corpsecret;
            this.key = key;
            this.domain = domain;
            this.keypwd = keypwd;
        }

        public string corpid { get; set; }
        public string corpsecret { get; set; }
        public string key { get; set; }
        public string domain { get; set; }

        /// <summary>
        /// 验证服务器是否可以接收消息。
        /// </summary>
        /// <returns></returns>
        public bool ValidServerUrl()
        {
            var echostr = HttpContext.Current.Request["echoStr"];
            dataloger.info(HttpContext.Current.Request.Url.ToString());
            if (!string.IsNullOrEmpty(echostr))
            {
                string sEchoStr = "";
                if (checkSignature(key, ref sEchoStr))
                {
                    HttpContext.Current.Response.Write(sEchoStr);//推送
                    return true;
                }

                throw new Exception("验证失败!");
            }
            else
            {
                //if (!checkSignature(key))
                //    throw new Exception("验证失败!");
            }

            return false;
        }



        /// <summary>
        /// access_token 这是是基础access_token
        /// </summary>
        /// <param name="access_token"></param>
        /// <returns></returns>
        public Ticket GetTicket(string access_token)
        {
            return WeiXin_Fun.GetCacheValue<Ticket>(corpid,
                (token1) =>
                {
                    return token1.expires_in <= WeiXin_Fun.DateTimeToStamp(DateTime.Now);
                },
            () =>
            {
                string ret = WeiXin_Fun.GetUrl(
                    "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token="
                    + access_token
                    +"");
                if (ret.Contains("ticket"))
                {
                    var v =
                    JavaScriptConvert.DeserializeObject<Ticket>(ret);

                    dataloger.info("public Token GetTicket():" + ret);

                    //过期时间
                    v.expires_in = WeiXin_Fun.DateTimeToStamp(DateTime.Now) + 1800;// +v.expires_in;
                    return v;
                }

                throw ThrowErr("获取GetTicket失败", ret);
            });
        }

        /// <summary>
        /// 只允许微信服务器访问自己的安全网络
        /// </summary>
        /// <param name="ACCESS_TOKEN"></param>
        /// <returns></returns>
        public getcallbackip GetCallBackip(string ACCESS_TOKEN)
        {
            string ret = WeiXin_Fun.GetUrl(
                    "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token="
                    + ACCESS_TOKEN);
            if (ret.Contains("ip_list"))
            {
                return (getcallbackip)JavaScriptConvert.DeserializeObject<getcallbackip>(ret);
            }

            throw ThrowErr("获取GetCallBackip失败：", ret);
        }

        

        /// <summary>
        /// 如果用户未关注，可能得到的信息不完整，但是可以通过这个信息得到用户是否已经关注
        /// </summary>
        /// <param name="openid"></param>
        /// <returns></returns>
        public QYH_USEROROPEN GetInfo(string code, string access_token)
        {
            //这里的token是基础token
            string ret = WeiXin_Fun.GetUrl(
                "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token="
                + access_token
                + "&code="
                + code
                + "");
            if (ret.Contains("UserId"))
            {
                var t = JavaScriptConvert.DeserializeObject<QYH_USERID>(ret);
                return new QYH_USEROROPEN()
                {
                    DeviceId = t.DeviceId,
                    UserId = t.UserId
                };
            }

            if (ret.Contains("OpenId"))
            {
                var t = JavaScriptConvert.DeserializeObject<QYH_OPENID>(ret);
                return new QYH_USEROROPEN()
                {
                    DeviceId = t.DeviceId,
                    OpenId = t.OpenId
                };
            }

            throw ThrowErr("获取GetInfo失败：", ret);
        }

        /// <summary>
        /// 不管用户是否关注，通过这个接口都是可以来取到用户信息
        /// </summary>
        /// <param name="access_token">这是网页授权token 跟基础token不一样</param>
        /// <param name="openid"></param>
        /// <returns></returns>
        public QYH_RetUserInfo GetUserInfo(string access_token, string userid)
        {
            //这里的token是基础token
            string ret = WeiXin_Fun.GetUrl(
                "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token="
                + access_token
                + "&userid="
                + userid
                + "");
            if (ret.Contains("\"userid\""))
            {
                return (QYH_RetUserInfo)JavaScriptConvert.DeserializeObject<QYH_RetUserInfo>(ret);
            }

            throw ThrowErr("获取GetUserInfo失败：", ret);
        }

        Exception ThrowErr(string msg, string ret)
        {
            dataloger.fatal(msg + ret);
            return new Exception(msg);
        }

        /// <summary>
        /// 字符串排序组合
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        string SortString(params string[] arg)
        {
            string[] arr = new string[arg.Length];
            int i = 0;
            foreach (string s in arg)
                arr[i++] = s;

            Array.Sort(arr);

            return string.Join("", arr);
        }

        /// <summary>
        /// SHA1加密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        string SHA1(string str)
        {
            return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(str, "SHA1");
        }

        bool checkSignature(string Token, ref string sEchoStr)
        {
            var signature = HttpContext.Current.Request["msg_signature"];
            var timestamp = HttpContext.Current.Request["timestamp"];
            var nonce = HttpContext.Current.Request["nonce"];

            Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(Token, this.keypwd, this.corpid);

            // string sVerifyMsgSig = HttpUtils.ParseUrl("msg_signature");
            string sVerifyMsgSig = HttpContext.Current.Request["msg_signature"];//"5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
            // string sVerifyTimeStamp = HttpUtils.ParseUrl("timestamp");
			string sVerifyTimeStamp = HttpContext.Current.Request["timestamp"];//"1409659589";
			// string sVerifyNonce = HttpUtils.ParseUrl("nonce");
            string sVerifyNonce = HttpContext.Current.Request["nonce"];//"263014780";
			// string sVerifyEchoStr = HttpUtils.ParseUrl("echostr");
            string sVerifyEchoStr = HttpContext.Current.Request["echostr"];//"P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";
            int ret = 0;
            

            ret = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr, ref sEchoStr);
            return ret == 0;



            ////string _corpid = "";

            ////var nonceA =
            ////    Tencent.Cryptography.AES_decrypt(
            ////    HttpContext.Current.Request["echostr"],
            ////    keypwd,
            ////    ref _corpid);
            //var echostr = HttpContext.Current.Request["echostr"];

            //var token = Token;

            //return Tencent.WXBizMsgCrypt.VerifySignature(
            //    token,
            //    timestamp,
            //    nonce,
            //    keypwd,
            //    signature) == 0;


            //string tmpStr = SortString(token, timestamp, nonce);
            //dataloger.info(tmpStr);
            //tmpStr = SHA1(tmpStr).ToLower();
            //dataloger.info(tmpStr);
            //return tmpStr.Equals(signature, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// 处理消息
        /// </summary>
        /// <param name="content"></param>
        public void Event(Type t, string content)
        {
            //普通消息
            if (content.Contains("<MsgId>"))
            {
                var json = WeiXin_Fun.Xml2Json(content);
                Message Msg = GetContentMessage(content, json);

                switch (Msg.MsgType)
                {
                    case "text":
                        WeiXin_Message.textAction((textMessage)Msg);
                        break;
                    case "image":
                        WeiXin_Message.imageAction((imageMessage)Msg);
                        break;
                    case "voice":
                        WeiXin_Message.voiceAction((voiceMessage)Msg);
                        break;
                    case "video":
                        WeiXin_Message.videoAction((videoMessage)Msg);
                        break;
                    case "shortvideo":
                        WeiXin_Message.shortvideoAction((shortvideoMessage)Msg);
                        break;
                    case "link":
                        WeiXin_Message.linkAction((linkMessage)Msg);
                        break;
                    case "location":
                        WeiXin_Message.locationAction((locationMessage)Msg);
                        break;
                }

                return;
            }

            if (content.Contains("<Event>"))
            {
                var json = WeiXin_Fun.Xml2Json(content);
                EventMessage Msg = GetContentEventMessage(content, json);

                switch (Msg.Event)
                {
                    case "subscribe":
                        WeiXin_Message.subscribeEventAction((subscribeEventMessage)Msg);
                        break;
                    case "SCAN":
                        WeiXin_Message.SCANEventAction((SCANEventMessage)Msg);
                        break;
                    case "LOCATION":
                        WeiXin_Message.LOCATIONEventAction((LOCATIONEventMessage)Msg);
                        break;
                    case "CLICK":
                        WeiXin_Message.CLICKEventAction((CLICKEventMessage)Msg);
                        break;
                    case "VIEW":
                        WeiXin_Message.VIEWEventAction((VIEWEventMessage)Msg);
                        break;

                }
            }
        }

        Message GetContentMessage(string content, string json)
        {
            Message Msg = null;

            if (content.Contains("<MsgType><![CDATA[shortvideo]]></MsgType>"))
                Msg = JavaScriptConvert.DeserializeObject<shortvideoMessage>(json);
            if (content.Contains("<MsgType><![CDATA[text]]></MsgType>"))
                Msg = JavaScriptConvert.DeserializeObject<textMessage>(json);
            if (content.Contains("<MsgType><![CDATA[image]]></MsgType>"))
                Msg = JavaScriptConvert.DeserializeObject<imageMessage>(json);
            if (content.Contains("<MsgType><![CDATA[voice]]></MsgType>"))
                Msg = JavaScriptConvert.DeserializeObject<voiceMessage>(json);
            if (content.Contains("<MsgType><![CDATA[video]]></MsgType>"))
                Msg = JavaScriptConvert.DeserializeObject<videoMessage>(json);
            if (content.Contains("<MsgType><![CDATA[link]]></MsgType>"))
                Msg = JavaScriptConvert.DeserializeObject<linkMessage>(json);
            if (content.Contains("<MsgType><![CDATA[location]]></MsgType>"))
                Msg = JavaScriptConvert.DeserializeObject<locationMessage>(json);

            return Msg;
        }

        EventMessage GetContentEventMessage(string content, string json)
        {
            EventMessage Msg = null;

            if (content.Contains("<Event><![CDATA[subscribe]]></Event>"))
                Msg = JavaScriptConvert.DeserializeObject<subscribeEventMessage>(json);
            if (content.Contains("<Event><![CDATA[SCAN]]></Event>"))
                Msg = JavaScriptConvert.DeserializeObject<SCANEventMessage>(json);
            if (content.Contains("<Event><![CDATA[LOCATION]]></Event>"))
                Msg = JavaScriptConvert.DeserializeObject<LOCATIONEventMessage>(json);
            if (content.Contains("<Event><![CDATA[CLICK]]></Event>"))
                Msg = JavaScriptConvert.DeserializeObject<CLICKEventMessage>(json);
            if (content.Contains("<Event><![CDATA[VIEW]]></Event>"))
                Msg = JavaScriptConvert.DeserializeObject<VIEWEventMessage>(json);


            return Msg;
        }


        /// <summary>
        /// 处理关系
        /// </summary>
        public void JSSDK_FX()
        {
//            朋友圈   from=timeline&isappinstalled=0
//微信群   from=groupmessage&isappinstalled=0
//好友分享 from=singlemessage&isappinstalled=0
            var from = HttpContext.Current.Request.QueryString["from"];
            var isappinstalled = HttpContext.Current.Request.QueryString["isappinstalled"];

            //记录用户的分享关系

        }

        /// <summary>
        /// access_token是公众号的全局唯一票据，公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时，需定时刷新，重复获取将导致上次获取的access_token失效。
        /// </summary>
        /// <returns></returns>
        public Token GetToken()
        {
            return WeiXin_Fun.GetCacheValue<Token>(corpid,
                (token1) =>
                {
                    return token1.expires_in <= WeiXin_Fun.DateTimeToStamp(DateTime.Now);
                },
            () =>
            {
                string ret = WeiXin_Fun.GetUrl(
                    "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="
                    + corpid
                    + "&corpsecret="
                    + corpsecret
                    + "");
                if (ret.Contains("access_token"))
                {
                    var v =
                    JavaScriptConvert.DeserializeObject<Token>(ret);

                    dataloger.info("public Token GetToken():" + ret);

                    //过期时间
                    v.expires_in = WeiXin_Fun.DateTimeToStamp(DateTime.Now) + 1800;//+ v.expires_in;
                    return v;
                }

                throw ThrowErr("获取GetToken失败", ret);
            });
        }

        /// <summary>
        /// 访问的链接地址，一般是request.url地址。
        /// </summary>
        /// <param name="link"></param>
        /// <returns></returns>
        public Dictionary<string, string> JSSDK()
        {
            //这个地址跟访问的地址一定要保持一致
            var url = HttpContext.Current.Request.Url.AbsoluteUri;
            Dictionary<string, string> keys = new Dictionary<string, string>();
            //得到token
            var token = GetToken();
            var ticket = GetTicket(token.access_token);

            
            keys["noncestr"] = WeiXin_Fun.CreateNonceStr();
            keys["url"] = "http://" + domain + HttpContext.Current.Request.Url.PathAndQuery;
            keys["jsapi_ticket"] = ticket.ticket;
            keys["timestamp"] = WeiXin_Fun.DateTimeToStamp(DateTime.Now)+"";

            string[] arr = new string[4];
            int i = 0;
            foreach (string s in keys.Keys)
                arr[i++] = s + "=" + keys[s];

            Array.Sort(arr);

            keys["appid"] = corpid;
            keys["signature"] = SHA1( string.Join("&", arr));
            ////分享出去的地址，可以不用考虑跟url一致
            //keys["link"] = link;
            //输出信息
            return keys;
        }


        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="p"></param>
        public string SendTextContent(string p)
        {
            var token = GetToken();
            var text = WeiXin_Fun.GetUrl(
                "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token="
                + token.access_token, true, "utf-8",
@"{
   ""filter"":{
      ""is_to_all"":false,
      ""group_id"":""""
   },
   ""text"":{
      ""content"":""CONTENT""
   },
    ""msgtype"":""text""
}".Replace("\r", "").Replace("\n", ""));

            return text;
        }

       

        public void RedirectWeiXin(string url, string scope = "snsapi_userinfo")
        {
            HttpContext.Current.Response.Redirect(
                "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
                + corpid
                + "&redirect_uri="
                + System.Web.HttpUtility.UrlEncode(url)
                + "&response_type=code&scope="
                + scope
                + "&state=STATE#wechat_redirect");
        }

        public QYH_UserIdToOpenId GetOpenId(string access_token,string user_id)
        {
            //https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid?access_token=ACCESS_TOKEN
            //这里的token是基础token
            //https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid?access_token=ACCESS_TOKEN
            string ret = WeiXin_Fun.GetUrl(
                "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid?access_token="
                + access_token,true,"utf-8",@"{
   ""userid"": """+user_id+@""",
}");
            if (ret.Contains("openid"))
            {
                return JavaScriptConvert.DeserializeObject<QYH_UserIdToOpenId>(ret);
               
            }


            throw ThrowErr("获取GetOpenId失败：", ret);
        }

        public string keypwd { get; set; }
    }
}